package org.chen.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.chen.auth.mapper.UserMapper;
import org.chen.auth.service.UserService;
import org.chen.auth.utils.Md5Util;
import org.chen.common.auth.dto.LoginRequest;
import org.chen.common.auth.dto.RegisterRequest;
import org.chen.common.auth.entity.User;
import org.chen.common.exception.BusinessException;
import org.chen.common.result.ResultVO;
import org.chen.common.scene.entity.District;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import cn.dev33.satoken.util.SaResult;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public ResultVO login(LoginRequest loginRequest) {
        try {
            // 判断是邮箱登录还是用户名
            User user;
            if (loginRequest.getUsername() != null && !loginRequest.getUsername().isEmpty()) {
                //根据用户名从数据库中查询
                user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getUsername,loginRequest.getUsername()));
            } else {
                user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getEmail,loginRequest.getEmail()));
            }

            if(user == null){
                return ResultVO.fail(102, "账号或密码错误");
            }

            //对登录密码进行加密
            boolean result = Md5Util.passwordVerify(loginRequest.getPassword(), user.getPassword(), Md5Util.md5Key);
            if(result){
                //根据用户id登录，第1步，先登录上
                StpUtil.login(user.getId());
                // 第2步，获取 Token  相关参数
                SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
                // 第3步，返回给前端
                Map<String, Object> stringObjectMap = new HashMap<>();
                stringObjectMap.put("token", tokenInfo.getTokenValue());

                Map<String, Object> userInfo = Map.of(
                        "id", user.getId(),
                        "email", user.getEmail(),
                        "name", user.getUsername()
                );

                stringObjectMap.put("userInfo", userInfo);
                return ResultVO.success(stringObjectMap);
            }
            return ResultVO.fail(103, "账号或密码错误");
        } catch (Exception e) {
            log.error(e.toString());
            throw new RuntimeException(e);
        }
    }

    @Override
    public ResultVO logout() {
        StpUtil.logout();
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        Boolean isLogin = tokenInfo.isLogin;
        if(!isLogin){
            return ResultVO.success();
        }
        return ResultVO.fail("退出失败");
    }

    @Override
    public Map<String, Object> getUserInfo(String token) {
        String loginIdByToken = StpUtil.getLoginIdByToken(token).toString();
        if (loginIdByToken != null) {
            // 获取用户信息
            User user = this.getById(loginIdByToken);
            if (user != null) {
                return Map.of(
                        "id", user.getId(),
                        "email", user.getEmail(),
                        "name", user.getUsername()
                );
            }
        }
        throw new BusinessException("用户不存在或密码错误");
    }

    @Override
    public String refreshToken(String token) {
        try {
            // 1. 验证当前token是否有效
            Object loginId = StpUtil.getLoginIdByToken(token);
            if (loginId == null) {
                throw new BusinessException("Token无效或已过期");
            }

            // 2. 检查token是否即将过期（比如剩余时间少于1小时）
            long timeout = StpUtil.getTokenTimeout(token);
            if (timeout == -1 || timeout > 3600) { // 如果永不过期或剩余时间大于1小时，不需要刷新
                return token; // 返回原token
            }

            // 3. 生成新的token（先临时登录该用户）
            StpUtil.login(loginId);
            SaTokenInfo newTokenInfo = StpUtil.getTokenInfo();

            return newTokenInfo.getTokenValue();

        } catch (Exception e) {
            throw new BusinessException("Token刷新失败：" + e.getMessage());
        }
    }

    @Override
    public ResultVO register(RegisterRequest registerRequest) {
        try {
            //根据用户名从数据库中查询
            User user = this.getOne(Wrappers.lambdaQuery(User.class).eq(User::getUsername,registerRequest.getUsername()));
            if(user != null){
                return ResultVO.fail("用户名已存在");
            }

            String pass = Md5Util.md5(registerRequest.getPassword(), Md5Util.md5Key);

            User registerUser = new User();

            registerUser.setUsername(registerRequest.getUsername());
            registerUser.setPassword(pass);
            registerUser.setEmail(registerRequest.getEmail());
            registerUser.setCreatedAt(LocalDateTime.now());

            saveSafely(registerUser);

            return  ResultVO.success();
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }

    private void saveSafely(User register){
        String lockKey = "register_lock:" + register.getUsername();
        String lockValue = UUID.randomUUID().toString();

        try {
            // 尝试获取锁
            Boolean lockAcquired = redisTemplate.opsForValue()
                    .setIfAbsent(lockKey, lockValue, Duration.ofSeconds(10));

            if (Boolean.TRUE.equals(lockAcquired)) {
                // 再次检查是否存在
                User existing = this.getOne(
                        new QueryWrapper<User>().eq("username", register.getUsername()));

                if (existing == null) {
                    save(register);
                }
            }
        } catch (Exception e) {
            // 释放锁
            String currentValue = redisTemplate.opsForValue().get(lockKey);
            if (lockValue.equals(currentValue)) {
                redisTemplate.delete(lockKey);
            }

            throw new RuntimeException(e);
        }
    }

}
